#ifndef SOLUTION_HPP_
#	define SOLUTION_HPP_

#	include "exc.hpp"
#   include "VRPTW.hpp"

#   include <deque>
#   include <map>

#   include "Tournee.hpp"

#   define MAX(A, B)   ((A > B) ? A : B)

struct SolutionEval
{    
    unsigned                                seNTournees;
    double                                  seTime;
    double									seDistance;
    double									seWait;
    double									seAdvantage;
    
    SolutionEval()
    {
		memset(this, 0, sizeof(SolutionEval));
	}
    std::string     toString() const;
    
    double          getHD() const
    {
        return ( 1.0 * this->seTime);
    }
    
    friend bool 	operator==(const SolutionEval& in1, const SolutionEval& in2);
    friend bool		operator<(const SolutionEval& in1, const SolutionEval& in2);
    friend bool		operator<=(const SolutionEval& in1, const SolutionEval& in2);
};

class Solution
{
	public:
        typedef enum { TOURNEE_VALID = 0, TOURNEE_UNKNOWN } TourneeState;
        
		Solution(VRPTW& inVRPTW);
        Solution(const Solution& inSolution);
		~Solution();
		
		Tournee&	operator[](unsigned inIndex) EXC_THROWER();
		const Tournee&	operator[](unsigned inIndex) const EXC_THROWER()
		{
			if (soTournees.size() == 0)
				EXC_THROW("unable to access, solution is empty");
				
			return (soTournees[inIndex % soTournees.size()]);
		}
		
		Solution&	operator=(const Solution& inSol)
		{
            if (this == &inSol)
                return (*this);
            
			soTournees = inSol.soTournees;
			soTourneesTmp = inSol.soTourneesTmp;
			soStates = inSol.soStates;
			soTourneeByClient = inSol.soTourneeByClient;
			soClientsToServe = inSol.soClientsToServe;
			return (*this);
		}
		
        void            validateTournee(unsigned inIndex) EXC_THROWER();
        void            restoreTournee(unsigned inIndex) EXC_THROWER();
        
        void			swapTournees(unsigned inIndex1, unsigned inIndex2);
        
        std::string     toString();
        
        void            eval(SolutionEval* inSolutionEval);
        
        bool			hasClient(unsigned inClient) const;
        
        bool            isComplete() const;
        
        void            complete();
        
        void			clear();
        
        void			fillDeque(std::deque<unsigned>& inDeque) const;
        
        unsigned		getPredecessorOf(unsigned inClient) const EXC_THROWER();
        
        
        unsigned		getTourneeByClient(unsigned inClient) EXC_THROWER();
        
        std::pair<unsigned, unsigned>        getRandomPair() const;
        void                                 buildTourneeBySize(std::multimap<unsigned, unsigned>& inTourneeBySize) const;
        void                                 buildTourneeByWait(std::multimap<double, unsigned>& inTourneeByWait, double inSensivity = 1.0);
        
        void            randomSwap();
        
        unsigned        getEmptyRandomTournee() const;
        unsigned        getFilledRandomTournee() const;
        
        unsigned        getFirstFilledFrom(unsigned i) const;
        void            reorganize();
        void			reorganizeBySizeCouples();
        void			reorganizeByWAFactor();
        
        void            shift();
        void			shuffle();
        
        void			printTournee(const std::string& inFilename, unsigned inTournee);
        
	protected:
        VRPTW&                          soVRPTW;
		std::deque<Tournee>	            soTournees;
        std::deque<Tournee>             soTourneesTmp;  // historique de taille 1 pour cancel si tournee s'invalide par une modification
        std::deque<unsigned>            soStates;
        std::deque<unsigned>            soTourneeByClient;
        std::map<unsigned, unsigned>    soClientsToServe;
};

#endif
